﻿#include <yangwebrtc/YangRtcSession.h>
#include <yangrtp/YangRtcpCompound.h>
#include <yangrtp/YangRtpConstant.h>
#include <yangwebrtc/YangCRtcStun.h>


#include <yangutil/yang_unistd.h>
#include <yangutil/sys/YangSsrc.h>
#include <yangutil/sys/YangLog.h>
#include <yangutil/sys/YangSRtp.h>

#include <yangwebrtc/YangRtcPlayStream.h>
#include <yangwebrtc/YangRtcPublishStream.h>

int32_t yang_rtcsession_dispatch_rtcp(YangRtcSession* session,YangRtcpCommon *rtcp);
/**
bool yang_is_rtp_or_rtcp(const uint8_t *data, size_t len) {
	return (len >= 12 && (data[0] & 0xC0) == 0x80);
}
// For STUN packet, 0x00 is binding request, 0x01 is binding success response.


// For RTCP, PT is [128, 223] (or without marker [0, 95]).
// Literally, RTCP starts from 64 not 0, so PT is [192, 223] (or without marker [64, 95]).
// @note For RTP, the PT is [96, 127], or [224, 255] with marker.
bool yang_is_rtcp(const uint8_t *data, size_t len) {
	return (len >= 12) && (data[0] & 0x80) && (data[1] >= 192 && data[1] <= 223);
}
**/
bool yang_is_stun(const uint8_t *data, size_t size) {
	return size > 0 && (data[0] == 0 || data[0] == 1)?1:0;
}
// change_cipher_spec(20), alert(21), handshake(22), application_data(23)
// @see https://tools.ietf.org/html/rfc2246#section-6.2.1
bool yang_is_dtls(const uint8_t *data, size_t len) {
	return (len >= 13 && (data[0] > 19 && data[0] < 64))?1:0;
}


void g_session_receive(char *data, int32_t nb_data,void* user){
	if(user==NULL) return;
	YangRtcSession* session=(YangRtcSession*)user;

	yang_rtcsession_receive(session,data, nb_data);
}

void g_startStunTimer(void* user){
	if(user==NULL) return;
	YangRtcSession* session=(YangRtcSession*)user;
	yang_rtcsession_startStunTimer(session);
	//session->startStunTimer();
}


void g_yang_doTask(int32_t taskId,void* user) {
	YangRtcSession* session=(YangRtcSession*)user;
	if (session->isSendStun && taskId == 1) {
		if (session->context&& session->context->stun.data && session->context->stun.nb > 0){
			yang_rtc_sendData(session->context->udp,session->context->stun.data, session->context->stun.nb);
		}

	}
	if (!session->startRecv)
		return;
	if (session->context->playStream) {
		//int32_t err=0;
       // if (taskId == 20) {
		//	session->play->check_send_nacks();
		//}
		if (taskId == 1) {

			if (yang_rtcplay_send_rtcp_rr(session->context,session->context->playStream))
				yang_error("RTCP Error:RR err ");
			if (yang_rtcplay_send_rtcp_xr_rrtr(session->context,session->context->playStream))
				yang_error("RTCP Error:XR err ");
		}
		if (taskId == 100) {
			if (yang_rtcplay_send_periodic_twcc(session->context,session->context->playStream))
				yang_error("RTCP Error:send twcc err ");
			//if(session->play->request_keyframe(this->session->videoSsrc)) yang_error("RTCP Error:request keyframe  err ");
		}
	}
}
void yang_init_rtcsession_context(YangRtcSession* session,void* user){
	if(session==NULL) return;
	session->context=(YangRtcContext*)calloc(1,sizeof(YangRtcContext));
	yang_init_rtcContext(session->context);
	session->context->user=user;
	session->context->udp->user=session;
	session->context->udp->receive=g_session_receive;
	session->context->udp->startStunTimer=g_startStunTimer;
}
void yang_init_rtcsession(YangRtcSession* session,YangStreamOptType role){
	if(session==NULL) return;




	session->codec=(YangVideoCodec)session->context->context->video->videoEncoderType;
	session->isSendDtls = 0;
	session->twcc_id_ = 1;
			//nack_enabled_ = 0;
			//session->play = NULL;
			//session->publish = NULL;
			session->h264Encoder=NULL;

			if(session->codec==Yang_VED_264){
				session->h264Encoder=(YangH264RtpEncode*)calloc(1,sizeof(YangH264RtpEncode));//new YangH264RtpEncode();
			}
		#if	Yang_H265_Encoding
			session->h265Encoder=NULL;
			if(session->codec==Yang_VED_265){
				session->h265Encoder=(YangH265RtpEncode*)calloc(1,sizeof(YangH265RtpEncode));//new YangH265RtpEncode();
			}
		#endif
		  //  session->20ms = new YangTimer();
		  //  session->20ms->setTaskId(20);
		   // session->20ms->setTimelen(20);
		  //  session->20ms->setTask(this);

			session->tm_1s = (YangCTimer*)calloc(1,sizeof(YangCTimer));//new YangCTimer();
			yang_init_timer(session->tm_1s,session,1,1000);
			session->tm_1s->doTask=g_yang_doTask;


			session->tm_100ms = (YangCTimer*)calloc(1,sizeof(YangCTimer));//new YangTimer();
			yang_init_timer(session->tm_100ms,session,100,100);
			session->tm_100ms->doTask=g_yang_doTask;
				//session->udp = NULL;
			session->startRecv = 0;

			//session->stunBuffer = new char[1024];
			//session->stunBufferLen = 0;

			session->isSendStun = 0;

			session->rtpBuffer=(YangRtpBuffer*)malloc(sizeof(YangRtpBuffer));
		    session->activeState=true;

		    yang_init_rtcdtls(session->context->dtls);

		    	yang_init_rtpBuffer(session->rtpBuffer,1500,1500);
		    	if(role==Yang_Stream_Publish){
		    		if(session->h264Encoder){
		    			yang_init_h264RtpEncode(session->h264Encoder,session->rtpBuffer);
		    		}
		    #if	Yang_H265_Encoding
		    		if(session->h265Encoder){
		    			yang_init_h265RtpEncode(session->h265Encoder,session->rtpBuffer);
		    		}
		    #endif
		    	}
		    	if (role == Yang_Stream_Play) {
		    		if (session->context->playStream == NULL){
		    			session->context->playStream = (YangRtcPlayStream*)calloc(1,sizeof(YangRtcPlayStream));
		                yang_init_rtcplay(session->context,session->context->playStream ,session->rtpBuffer);
		    		//session->play->session->twcc_enabled
		    		}
		    	} else {
		    		if (session->context->pubStream == NULL){
		    			session->context->pubStream = (YangRtcPublishStream*)calloc(1,sizeof(YangRtcPublishStream));//new YangRtcPublishStream(this);
		    			yang_init_rtcpublish(session->context->pubStream,session->context->audioSsrc,session->context->videoSsrc);

		    		}
		    	}
}
void yang_destroy_rtcsession(YangRtcSession* session){
	if(session==NULL) return;
	 session->activeState=false;
	    //yang_stop(session->20ms);
		yang_timer_stop(session->tm_1s);
		yang_timer_stop(session->tm_100ms);
		yang_destroy_timer(session->tm_1s);
		yang_destroy_timer(session->tm_100ms);
	   // yang_delete(session->20ms);
		yang_free(session->tm_1s);
		yang_free(session->tm_100ms);

		yang_destroy_h264RtpEncode(session->h264Encoder);
		yang_free(session->h264Encoder);
	#if	Yang_H265_Encoding
		yang_destroy_h265RtpEncode(session->h265Encoder);
		yang_free(session->h265Encoder);
	#endif
		//session->udp = NULL;
		//session->srtp = NULL;
		//session->context=NULL;
		yang_destroy_rtcContext(session->context);
		yang_free(session->context);
		yang_destroy_rtpBuffer(session->rtpBuffer);
		yang_free(session->rtpBuffer);
}



void yang_rtcsession_setSsrc(YangRtcSession* session,uint32_t audioSsrc,uint32_t videoSsrc){
	if(session->h264Encoder){
		session->h264Encoder->audioSsrc=audioSsrc;
		session->h264Encoder->videoSsrc=videoSsrc;
	}
#if	Yang_H265_Encoding
	if(session->h265Encoder){
		session->h265Encoder->audioSsrc=audioSsrc;
		session->h265Encoder->videoSsrc=videoSsrc;
	}
#endif

}


void yang_rtcsession_startStunTimer(YangRtcSession* session) {
	if (session->tm_1s) yang_timer_start(session->tm_1s);

	session->isSendStun = 1;
}
void yang_rtcsession_startTimers(YangRtcSession* session) {
   // if (session->20ms&&!session->20ms->session->isStart)		session->20ms->start();
	if (session->context->context&&session->context->context->rtc->sendTwcc&&session->tm_100ms&&!session->tm_100ms->isStart)
		yang_timer_start(session->tm_100ms);//->start();
}
//void YangRtcSession::setStunBuffer(char *p, int32_t plen) {
//	memcpy(session->stunBuffer, p, plen);
//	session->stunBufferLen = plen;
//}
int32_t yang_rtcsession_on_rtcp(YangRtcSession* session,char *data, int32_t nb_data) {
	int32_t err = Yang_Ok;

	int32_t nb_unprotected_buf = nb_data;
	if ((err = yang_dec_rtcp(&session->context->srtp,data, &nb_unprotected_buf)) != Yang_Ok) {
		return yang_error_wrap(err, "rtcp unprotect");
	}

	char *unprotected_buf = data;
	YangBuffer buffer ;
	yang_init_buffer(&buffer,unprotected_buf, nb_unprotected_buf);
	//YangAutoFree(YangBuffer, buffer);

	YangRtcpCompound rtcp_compound;
	yang_init_rtcpCompound(&rtcp_compound);
	if (Yang_Ok != (err = yang_decode_rtcpCompound(&rtcp_compound,&buffer))) {
		return yang_error_wrap(err, "decode rtcp plaintext=%u",
				nb_unprotected_buf);
	}

	YangRtcpCommon *rtcp = NULL;
	for(int i=0;i<rtcp_compound.vlen;i++){
		rtcp=(YangRtcpCommon*)(rtcp_compound.rtcps+sizeof(YangRtcpCommon)*i);
		err = yang_rtcsession_dispatch_rtcp(session,rtcp);
		//YangAutoFree(YangRtcpCommon, rtcp);

		if (Yang_Ok != err) {
			return yang_error_wrap(err,
					"cipher=%u, plaintext=%u,  rtcp=(%u,%u,%u,%u)", nb_data,
					nb_unprotected_buf, rtcp->nb_data, rtcp->header.rc,
					rtcp->header.type, rtcp->ssrc);
		}
	}
	yang_destroy_rtcpCompound(&rtcp_compound);
	return err;

}
void yang_rtcsession_setStun(YangRtcSession* session,char* data,int32_t nb){
	if(session==NULL||session->context==NULL) return;
	if(session->context->stun.data==NULL) session->context->stun.data=(char*)calloc(1,nb);
	memcpy(session->context->stun.data,data,nb);
	session->context->stun.nb=nb;
}
void yang_rtcsession_startudp(YangRtcSession* session,int32_t localport){
	yang_init_rtcudp(session->context->udp,session->context->streamConf->serverIp,localport);
	yang_init_rtcsession(session,session->context->streamConf->streamOptType);

	yang_rtcsession_setSsrc(session,session->context->audioSsrc,session->context->videoSsrc);
	yang_start_rtcudp(session->context->udp);
}
void yang_rtcsession_receive(YangRtcSession* session,char *data, int32_t size) {

    if(!session->activeState) return;

	bool is_rtp_or_rtcp = (size >= 12 && (data[0] & 0xC0) == 0x80);
	bool is_rtcp = (size >= 12) && (data[0] & 0x80) && (data[1] >= 192 && data[1] <= 223);


	if (is_rtp_or_rtcp&&!is_rtcp) {
		session->startRecv = 1;
		if (session->context&&session->context->playStream) 	yang_rtcplay_on_rtp(session->context,session->context->playStream,data, size);
		return;

	}
	if (is_rtp_or_rtcp && is_rtcp) {
		yang_rtcsession_on_rtcp(session,data, size);
		return;
	}

	if (!is_rtp_or_rtcp && yang_is_stun((uint8_t*)data,size)) {

		//YangStunPacket ping;
		int32_t err = 0;
		if ((err = yang_decode_rtcstun(data, size)) != Yang_Ok) {
			yang_error("decode stun packet failed");
			return;
		}

		if (!session->isSendDtls) {
			if (yang_startHandShake(session->context->dtls)) {
				yang_error("dtls start handshake failed!");
			}

			session->isSendDtls = 1;
		}
		return;
	}
	if (size >= 13 && yang_is_dtls((uint8_t*)data,size)) {

		if (session->context->dtls) {
			if (yang_decodeHandshake(session->context->dtls,data, size) == Yang_Ok) {

				if(session->context->dtls->state==YangDtlsStateClientDone) {
					yang_rtcsession_startTimers(session);
					session->context->state=1;

					if(session->context&&session->context->context)
						//session->context->context->streams.sendRequest(session->context->streamConf->uid,0,Yang_Req_Connected);
						session->context->context->yang_sendRequest(session->context->streamConf->uid,0,Yang_Req_Connected);
				}
			}
		}

	}
}


int32_t yang_rtcsession_send_video_meta(YangRtcSession* session,YangFrame* p)
{

     if(session->h264Encoder) return  yang_pub_h264_package_stap_a(session->context,session->h264Encoder,p);
#if	Yang_H265_Encoding
     if(session->h265Encoder) return yang_pub_h265_package_stap_a(session->context,session->h265Encoder,p);
#endif
     return Yang_Ok;
}

int32_t yang_rtcsession_publishVideo(YangRtcSession* session,YangFrame* p) {

	if(p->frametype==YANG_Frametype_Spspps) return yang_rtcsession_send_video_meta(session,p);
	if(session->h264Encoder) return yang_pub_h264_video(session->context,session->h264Encoder,p);
#if	Yang_H265_Encoding
	if(session->h265Encoder) return yang_pub_h265_video(session->context,session->h265Encoder,p);
#endif
	return Yang_Ok;

}

int32_t yang_rtcsession_publishAudio(YangRtcSession* session,YangFrame *p) {

	if(session->h264Encoder) return yang_pub_h264_audio(session->context,session->h264Encoder,p);
#if	Yang_H265_Encoding
	if(session->h265Encoder) return yang_pub_h265_audio(session->context,session->h265Encoder,p);
#endif
	//return session->h264Encoder.on_audio(p);
	return Yang_Ok;

}

void yang_rtcsession_disconnect(YangRtcSession* session) {
	if(session==NULL||session->context==NULL) return;
	if(session->context->dtls) yang_sendDtlsAlert(session->context->dtls);
}

//YangSRtp* YangRtcSession::get_srtp() {
	//return session->dtls->getSrtp();
//}


int32_t yang_rtcsession_send_rtcp(YangRtcSession* session,char *data, int32_t nb_data) {
	int32_t err = Yang_Ok;
	int32_t nb_buf = nb_data;

	if ((err = yang_enc_rtcp(&session->context->srtp,data, &nb_buf)) != Yang_Ok) {
		return yang_error_wrap(err, "protect rtcp");
	}

	if ((err = yang_rtc_sendData(session->context->udp,data, nb_buf)) != Yang_Ok) {
		return yang_error_wrap(err, "send");
	}

	return err;
}

int32_t yang_rtcsession_on_rtcp_feedback_twcc(YangRtcSession* session,char *data, int32_t nb_data) {
	return Yang_Ok;
}

int32_t yang_rtcsession_on_rtcp_feedback_remb(YangRtcSession* session,YangRtcpCommon *rtcp) {
	//ignore REMB
	return Yang_Ok;
}
void yang_rtcsession_do_request_keyframe(YangRtcSession* session,uint32_t ssrc){
	if(session->context&&session->context->context)
			session->context->context->yang_sendRequest(session->context->streamConf->uid,ssrc,Yang_Req_Sendkeyframe);

}

int32_t yang_rtcsession_dispatch_rtcp(YangRtcSession* session,YangRtcpCommon *rtcp) {
	int32_t err = Yang_Ok;
	uint16_t rtcpType=rtcp->header.type;
	// For TWCC packet.
	if (YangRtcpType_rtpfb == rtcpType && 15 == rtcp->header.rc) {
		return yang_rtcsession_on_rtcp_feedback_twcc(session,rtcp->data, rtcp->nb_data);
	}

	// For REMB packet.
	if (YangRtcpType_psfb == rtcpType) {
		//YangRtcpPsfbCommon *psfb = dynamic_cast<YangRtcpPsfbCommon*>(rtcp);
		if (15 == rtcp->header.rc){//psfb->get_rc()) {
			return yang_rtcsession_on_rtcp_feedback_remb(session,rtcp);
		}
	}

	// Ignore special packet.
	if (YangRtcpType_rr == rtcpType) {
		//YangRtcpRR *rr = (YangRtcpRR*)(rtcp);
		if (rtcp->rb->ssrc == 0) { //for native client
			return err;
		}
	}


	if (session->context->pubStream && Yang_Ok != (err = yang_rtcpublish_on_rtcp(session->context ,session->context->pubStream,rtcp))) {
		return yang_error_wrap(err, "handle rtcp");
	}
	if (session->context->playStream && Yang_Ok != (err = yang_rtcplay_on_rtcp(session->context ,session->context->playStream,rtcp))) {
		return yang_error_wrap(err, "handle rtcp");
	}

	return err;
}



